#!/usr/bin/env python

# release notes:
# 1.1
# - support for ID += ID | NUMBER


import lex, yacc, sys, re, sys

DEBUG = 0

reserved = {
		'vars'  : 'VARS',
		'var'   : 'VAR',
		'code'  : 'CODE',
		'while' : 'WHILE',
		'do'    : 'DO',
		'done'  : 'DONE',
		'if'    : 'IF',
		'ifn'   : 'IFN',
		'then'  : 'THEN',
		'fi'    : 'FI',
		'put'   : 'PUT',
		'get'   : 'GET',
		'print' : 'PRINT'
	}

tokens = [
		'ID',
		'EQUALS',
		'NUMBER',
		'PLUS',
		'MINUS',
		'TEXT',
		'PLUSEQUALS',
		'MINUSEQUALS',
		'OPENBRACKET',
		'CLOSEBRACKET'
	] + list(reserved.values())


t_OPENBRACKET = r'\['
t_CLOSEBRACKET = r'\]'
t_EQUALS = r'='
t_PLUSEQUALS = r'\+='
t_MINUSEQUALS = r'-='
t_PLUS = r'\+'
t_MINUS = r'-'

def t_ID(t):
	r'[a-zA-Z_][a-zA-Z_0-9]*'
	t.type = reserved.get(t.value, 'ID')
	return t

def t_NUMBER(t):
	r'\d+'
	t.value = int(t.value)
	return t

def t_newline(t):
	r'\n'
	t.lexer.lineno += len(t.value)

def t_COMMENT(t):
	r'\#.*'
	pass

def t_TEXT(t):
	r'"[^"\\]*(\\.[^"\\]*)*"'
	return t

t_ignore = ' \t'

def t_error(t):
	print "illegal character '%s'" % t.value[0]
	t.lexer.skip(1)

lex.lex()

tape_len = 30000
tmpvars = []
releasedvars = []
bf = ""
varcnt = 0
pos = 0
names = { }
types = { }

TYPE_INTEGER = 1
TYPE_STRING = 2

# TODO: error handling if running out of space
def get_tmp():
	r = 0
	if len(releasedvars) == 0:
		r = varcnt + len(tmpvars)
		tmpvars.append(r)
	else:
		r = releasedvars[-1]
		del releasedvars[-1]
		tmpvars.append(r)
	return r

def release_tmp(p):
	tmpvars.remove(p)
	releasedvars.append(p)

def add_bf(s, sfx = None):
	global bf
	bf += s
	if DEBUG:
		msg = "(" + s + ")"
		if sfx:
			msg += " " + sfx
		print msg

def jump_to_pos(p):
	global pos
	s = ""
	while pos > p:
		s += "<"
		pos -= 1
	while pos < p:
		s += ">"
		pos += 1
	return s

def jump_to_var(name):
	return jump_to_pos(names[name][1])

def set_val(name, val):
	px = pos
	s = jump_to_var(name)
	s += "[-]"
	for i in range(0, val):
		s += "+"
	s += jump_to_pos(px)
	if DEBUG:
		print "pos after set_val:", pos
	add_bf(s)

def set_pos_zero(p):
	px = pos
	s = jump_to_pos(p)
	s += "[-]"
	s += jump_to_pos(px)
	return s

def add_var(v1, v2):
	s = ""
	p = get_tmp()         # get tmp variable
	s += set_pos_zero(p)  # set tmp to zero, does not modify position on tape
	px = pos
	s += jump_to_var(v2)
	s += "["
	s += jump_to_var(v1)
	s += "+"
	s += jump_to_pos(p)
	s += "+"
	s += jump_to_var(v2)
	s += "-]"
	# now v1 = v1 + v2, tmp = v2, v2 = 0
	# move tmp to v2
	s += jump_to_pos(p)
	s += "["
	s += jump_to_var(v2)
	s += "+"
	s += jump_to_pos(p)
	s += "-]"
	s += jump_to_pos(px)
	add_bf(s)
	release_tmp(p)

def add_val(v1, val):
	s = ""
	px = pos
	s += jump_to_var(v1)
	for i in range(0, val):
		s += "+"
	s += jump_to_pos(px)
	add_bf(s)

def sub_var(v1, v2):
	s = ""
	p = get_tmp()         # get tmp variable
	s += set_pos_zero(p)  # set tmp to zero, does not modify position on tape
	px = pos
	s += jump_to_var(v2)
	s += "["
	s += jump_to_var(v1)
	s += "-"
	s += jump_to_pos(p)
	s += "+"
	s += jump_to_var(v2)
	s += "-]"
	# now v1 = v1 - v2, tmp = v2, v2 = 0
	# move tmp to v2
	s += jump_to_pos(p)
	s += "["
	s += jump_to_var(v2)
	s += "+"
	s += jump_to_pos(p)
	s += "-]"
	s += jump_to_pos(px)
	add_bf(s)
	release_tmp(p)

def sub_val(v1, val):
	s = ""
	px = pos
	s += jump_to_var(v1)
	for i in range(0, val):
		s += "-"
	s += jump_to_pos(px)
	add_bf(s)


def set_var(v1, v2):
	s = ""
	p = get_tmp()         # get tmp variable
	s += set_pos_zero(p)  # set tmp to zero, does not modify position on tape
	px = pos
	s += jump_to_var(v1)  # set v1 = 0
	s += "[-]"

	s += jump_to_var(v2)
	s += "["
	s += jump_to_var(v1)
	s += "+"
	s += jump_to_pos(p)
	s += "+"
	s += jump_to_var(v2)
	s += "-]"
	# now v1 = v2, tmp = v2, v2 = 0
	# move tmp to v2
	s += jump_to_pos(p)
	s += "["
	s += jump_to_var(v2)
	s += "+"
	s += jump_to_pos(p)
	s += "-]"
	s += jump_to_pos(px)
	add_bf(s)
	release_tmp(p)



def p_statement_define(t):
	'''statement : VARS vardefs CODE expressions
	             | VARS CODE expressions'''

def p_vardefs(t):
	'''vardefs : vardef 
	           | vardef vardefs'''

def p_vardef_int(t):
	'vardef : VAR ID EQUALS NUMBER'
	global varcnt
	names[t[2]] = [t[4], varcnt]
	types[t[2]] = TYPE_INTEGER
	varcnt += 1
	global pos
	s = ""
	for i in range(0, t[4]):
		s += "+"
	s += ">"
	add_bf(s)
	pos += 1

def p_vardef_str(t):
	'vardef : VAR ID OPENBRACKET NUMBER CLOSEBRACKET'
	global varcnt
	names[t[2]] = [t[4], varcnt]
	types[t[2]] = TYPE_STRING
	varcnt += int(t[4])
	global pos
	s = ""
	for i in range(0, t[4]):
		s += ">"
	add_bf(s)
	pos += int(t[4])

def p_expressions(t):
	'''expressions : expression
	               | expression expressions'''

def p_expression_id(t):
	'expression : ID EQUALS ID PLUS ID'
	if t[1] == t[3] or t[1] == t[5]:
		print "target must not be an operand"
		sys.exit(1)
	if t[1] not in names or t[3] not in names or t[5] not in names:
		print "lookup failed"
		sys.exit(1)
	set_val(t[1], 0)
	add_var(t[1], t[3])
	add_var(t[1], t[5])

def p_expression_num(t):
	'expression : ID EQUALS ID PLUS NUMBER'
	if t[1] == t[3]:
		print "target must not be an operand"
		sys.exit(1)
	if t[1] not in names or t[3] not in names:
		print "lookup failed"
		sys.exit(1)
	set_val(t[1], 0)
	add_var(t[1], t[3])
	add_val(t[1], t[5])

def p_expression_peq_id(t):
	'expression : ID PLUSEQUALS ID'
	if t[1] == t[3]:
		print "target must not be an operand"
		sys.exit(1)
	if t[1] not in names or t[3] not in names:
		print "lookup failed"
		sys.exit(1)
	add_var(t[1], t[3])

def p_expression_peq_num(t):
	'expression : ID PLUSEQUALS NUMBER'
	if t[1] not in names:
		print "lookup failed"
		sys.exit(1)
	add_val(t[1], t[3])


def p_expression_minq_id(t):
	'expression : ID MINUSEQUALS ID'
	if t[1] == t[3]:
		print "target must not be an operand"
		sys.exit(1)
	if t[1] not in names or t[3] not in names:
		print "lookup failed"
		sys.exit(1)
	sub_var(t[1], t[3])

def p_expression_min_eq_num(t):
	'expression : ID MINUSEQUALS NUMBER'
	if t[1] not in names:
		print "lookup failed"
		sys.exit(1)
	sub_val(t[1], t[3])



def p_expression_id_m(t):
	'expression : ID EQUALS ID MINUS ID'
	if t[1] == t[3] or t[1] == t[5]:
		print "target must not be an operand"
		sys.exit(1)
	if t[1] not in names or t[3] not in names or t[5] not in names:
		print "lookup failed"
		sys.exit(1)
	set_var(t[1], t[3])
	sub_var(t[1], t[5])

def p_expression_num_m(t):
	'expression : ID EQUALS ID MINUS NUMBER'
	if t[1] == t[3]:
		print "target must not be an operand"
		sys.exit(1)
	if t[1] not in names or t[3] not in names:
		print "lookup failed"
		sys.exit(1)
	set_var(t[1], t[3])
	sub_val(t[1], t[5])

def p_expression_put(t):
	'expression : PUT ID'
	if t[2] not in names:
		print "lookup failed"
		sys.exit(1)
	px = pos
	add_bf(jump_to_var(t[2]))
	add_bf(".")
	add_bf(jump_to_pos(px))

def p_expression_get(t):
	'expression : GET ID'
	if t[2] not in names:
		print "lookup failed"
		sys.exit(1)
	px = pos
	add_bf(jump_to_var(t[2]))
	add_bf(",")
	add_bf(jump_to_pos(px))

def p_assign_num(t):
	'expression : ID EQUALS NUMBER'
	if t[1] not in names:
		print "lookup failed"
		sys.exit(1)
	set_val(t[1], t[3])

def p_assign_var(t):
	'expression : ID EQUALS ID'
	if t[1] not in names or t[3] not in names:
		print "lookup failed"
		sys.exit(1)
	set_val(t[1], 0)
	add_var(t[1], t[3])

def p_print(t):
	'expression : PRINT TEXT'
	s = t[2][1:len(t[2])-1]
	px = pos
	p = get_tmp()
	add_bf(set_pos_zero(p))
	add_bf(jump_to_pos(p))
	val = 0
	for c in s.decode("string_escape"):
		while val > ord(c): 
			add_bf("-")
			val -= 1
		while val < ord(c): 
			add_bf("+")
			val += 1
		add_bf(".")
	add_bf(jump_to_pos(px))
	release_tmp(p)



def jumps(varname):
	px = pos
	a = jump_to_var(varname)
	b = jump_to_pos(px)
	return [a, b]


_stack = []




def p_expression_w(t):
	'expression : WHILE ID _seenIDw DO expressions DONE'
	global pos
	j = _stack[-1]
	del _stack[-1]
	pos = _stack[-1]
	del _stack[-1]
	add_bf("]" + j) # jump back to position before if/when

def p_expression_seenW(t):
	'_seenIDw :'
	global pos
	id = t[-1]
	if id not in names:
		print "lookup failed"
		sys.exit(1)
	global pos
	px = pos
	_stack.append(pos)
	add_bf(jump_to_var(id), "seenW")
	add_bf("[")
	x = pos
	_stack.append(jump_to_pos(px))
	pos = x
	t[0] = t[-1]



def p_expression_ifn(t):
	'expression : IFN ID _seenIDn THEN expressions FI'
	global pos
	release_tmp(_stack[-1])
	del _stack[-1]
	release_tmp(_stack[-1])
	del _stack[-1]
	add_bf("]" + _stack[-1]) # jump back to position before if
	del _stack[-1]
	pos = _stack[-1]
	del _stack[-1]
	
def p_expression_seenIFn(t):
	'_seenIDn :'
	id = t[-1]
	if id not in names:
		print "lookup failed"
		sys.exit(1)
	global pos
	px = pos
	_stack.append(pos)
	add_bf(jump_to_var(id), "seenID")
	t0 = get_tmp()
	t1 = get_tmp()
	add_bf(jump_to_pos(t0) + "[-]")   # t0 = 0
	add_bf(jump_to_pos(t1) + "[-]")   # t1 = 0
	add_bf(jump_to_var(id))   # copy id to t0 and t1
	add_bf("[" + jump_to_pos(t0) + "+" + jump_to_pos(t1) + "+" + jump_to_var(id) + "-]")
	add_bf(jump_to_pos(t0))
	add_bf("[" + jump_to_var(id) + "+" + jump_to_pos(t0) + "-]")
	add_bf("+")   # t0 = 1
	add_bf(jump_to_pos(t1))
	add_bf("[" + jump_to_pos(t0) + "[-]" + jump_to_pos(t1) + "[-]]") # t0 = 0, t1 = 0
	add_bf(jump_to_pos(t0))
	add_bf("[-")
	_stack.append(jump_to_pos(px))
	pos = t0
	_stack.append(t0)
	_stack.append(t1)
	t[0] = t[-1]



def p_expression_if(t):
	'expression : IF ID _seenID THEN expressions FI'
	global pos
	add_bf("[-]]" + _stack[-1]) # jump back to position before if
	del _stack[-1]
	pos = _stack[-1]
	del _stack[-1]
	
def p_expression_seenIF(t):
	'_seenID :'
	id = t[-1]
	if id not in names:
		print "lookup failed"
		sys.exit(1)
	global pos
	px = pos
	_stack.append(pos)
	add_bf(jump_to_var(id), "seenID")
	t0 = get_tmp()
	t1 = get_tmp()
	add_bf(jump_to_pos(t0))
	add_bf("[-]")
	add_bf(jump_to_pos(t1))
	add_bf("[-]");
	add_bf(jump_to_var(id))
	add_bf("[" + jump_to_pos(t0) + "+" + jump_to_pos(t1) + "+" + jump_to_var(id) + "-]")
	add_bf(jump_to_pos(t0))
	add_bf("[" + jump_to_var(id) + "+" + jump_to_pos(t0) + "-]")
	add_bf(jump_to_pos(t1))
	add_bf("[")
	_stack.append(jump_to_pos(px))
	pos = t1
	release_tmp(t0)
	release_tmp(t1)
	t[0] = t[-1]


def minimize(s):
	a = True
	while a:
		a = False
		if s.find("<>") >= 0:
			a = True
			s = re.sub(r'<>', "", s)
		if s.find("><") >= 0:
			a = True
			s = re.sub(r'><', "", s)
	return s

yacc.yacc()
s = sys.stdin.read();
#s = open("code.bk").read()
yacc.parse(s);

print minimize(bf)


